package logica;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Stack;

public class BuscaCustoUniforme {

	private Arvore arvore;
	private Vertice objetivo;
	private ArrayList<Vertice> visitados;
	private HashMap<Vertice, Direcao> direcoes;
	private boolean encontrou;
	private Direcao direcaoAtual;
	
	private static final int CUSTO_GIRO = 1;
	private static final int CUSTO_GIRO_DUPLO = 2;
	private final static int CUSTO_EXPLOSAO = 3;
	private final static int CUSTO_PASSO = 1;

	public ArrayList<Vertice> getVisitados() {
		return visitados;
	}

	private Stack <Vertice> pilha = new Stack<>();

	public Stack<Vertice> getPilha() {
		return pilha;
	}

	public BuscaCustoUniforme (Arvore arvore, Vertice objetivo, Direcao direcaoInicial) {
		this.arvore = arvore;
		this.objetivo = objetivo;
		this.direcaoAtual = direcaoInicial;
		this.visitados = new ArrayList<Vertice>();
		this.direcoes = new HashMap<Vertice, Direcao>();
	}

	public boolean buscar(Vertice origem) {
		//Adiciona o vertice origem e seus adjacentes a lista de vertices arvore
		arvore.adicionaVertice(origem);
		arvore.getAdjacentes(origem, true);
		Vertice[] vertices = arvore.getVerticesIniciais();
		
		
		for (Vertice v : vertices) {
			if (encontrou)
				return encontrou;
			pilha.push(v);
			direcoes.put(v, direcaoAtual);
			
			encontrou = v.equals(objetivo);
			if ((!encontrou) && (!v.foiVisitado())) {
				visitar(v);
			} else {
				return encontrou;
			}
		}
		return encontrou;
	}

	public boolean visitar(Vertice vert) {
		vert.setVisitado(true);
		visitados.add(vert);
		

		List<Vertice> adjacentesDoVertice = arvore.getAdjacentes(vert, true);
		Collections.sort(adjacentesDoVertice);
		//???? getMelhorVertice(adjacentesDoVertice);
		
		for (Vertice adj : adjacentesDoVertice) {
			if (encontrou)
				return true;
			else
				encontrou = adj.equals(objetivo);
			if (encontrou){
				visitados.add(adj);
				adj.setCusto(custoAdjacente(vert, adj));
				pilha.push(adj);
			
				return true;
			}

			if  (!adj.foiVisitado()) {
				pilha.push(adj);
				visitar(adj);
			}
		}
		if (!encontrou) {
			pilha.pop();
			//direcoes.remove(adj);
		}

		return encontrou;
	}

	public double custoAdjacente(Vertice atual, Vertice adjacente) {
		Celula celulaAdjacente = this.arvore.getCalabouco().getMatriz()[adjacente.getPosicao().getX()][adjacente.getPosicao().getY()];
		double custo = 0;
		double custoGiro = 0;//getValorDoGiro(adjacente.getPosicao(), adjacente.getPosicao());
		if (celulaAdjacente instanceof Catre) {
			custo = CUSTO_EXPLOSAO + CUSTO_PASSO + custoGiro;
		} else {
			custo = CUSTO_PASSO + custoGiro;
		}
		return custo;
	}

	private double getValorDoGiro(Posicao posicaoCelula, Posicao posicaoAdjacente) {
		Direcao direcaoRelativa = obterDirecaoRelativa(posicaoCelula, posicaoAdjacente);
		switch (direcaoAtual) {
		case NORTE:
			return (direcaoRelativa == Direcao.SUL) ? CUSTO_GIRO_DUPLO : (direcaoRelativa != direcaoAtual) ? CUSTO_GIRO : 0;
		case SUL:
			return (direcaoRelativa == Direcao.NORTE) ? CUSTO_GIRO_DUPLO : (direcaoRelativa != direcaoAtual) ? CUSTO_GIRO : 0;
		case LESTE:
			return (direcaoRelativa == Direcao.OESTE) ? CUSTO_GIRO_DUPLO : (direcaoRelativa != direcaoAtual) ? CUSTO_GIRO : 0;
		case OESTE:
			return (direcaoRelativa == Direcao.LESTE) ? CUSTO_GIRO_DUPLO : (direcaoRelativa != direcaoAtual) ? CUSTO_GIRO : 0;
		default:
			return 0;
		}
	}

	private Direcao obterDirecaoRelativa(Posicao posicaoAtual, Posicao posicaoAdjacente) {
		if (posicaoAdjacente.getX() == posicaoAtual.getX()){
			if (posicaoAdjacente.getY() > posicaoAtual.getY()){
				return Direcao.OESTE;
			} else {
				return Direcao.LESTE;
			}
		} else if (posicaoAdjacente.getY() == posicaoAtual.getY()){
			if (posicaoAdjacente.getX() > posicaoAtual.getX()){
				return Direcao.NORTE;
			} else {
				return Direcao.SUL;
			}
		}
		return null;
	}

	private Vertice getMelhorVertice(List<Vertice> adjacentes) {
		Vertice melhorVertice = adjacentes.get(0);
		for (Vertice adj : adjacentes) {
			if (custoAdjacente(melhorVertice, adj) < melhorVertice.getCusto()){
				melhorVertice = adj;
			}
		}
		return melhorVertice;
	}
}
